package com.thouiot.modbus.service.impl;

import com.serotonin.modbus4j.BasicProcessImage;
import com.serotonin.modbus4j.ModbusSlaveSet;
import com.serotonin.modbus4j.ProcessImage;
import com.thouiot.common.exception.TiotRuntimeException;
import com.thouiot.modbus.component.ModbusSlaveHolder;
import com.thouiot.modbus.config.ModbusTypeEnum;
import com.thouiot.modbus.entity.vo.ModbusLocatorVo;
import com.thouiot.modbus.entity.vo.SlaveImageVo;
import com.thouiot.modbus.entity.vo.WriteValueVo;
import com.thouiot.modbus.enums.RegisterTypeEnum;
import com.thouiot.modbus.enums.WriteValueTypeEnum;
import com.thouiot.modbus.exception.ModbusErrorCodeEnum;
import com.thouiot.modbus.service.ISlaveService;
import com.thouiot.modbus.util.ModbusConvertUtil;
import com.thouiot.modbus.util.ModbusParamsUtil;
import com.thouiot.modbus.util.ModbusSlaveUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

/**
 * @description modbus slave 操作
 * @author wangkai
 * @date 2025/04/15 15:25
 */
@Service
public class SlaveServiceImpl implements ISlaveService {

    @Resource
    private ModbusSlaveHolder modbusSlaveHolder;

    @Override
    public void writeSlave(ModbusLocatorVo locator) {

        ModbusParamsUtil.checkBaseParams(locator);

        String modbusType = locator.getModbusType();
        ModbusTypeEnum modbusTypeEnum = ModbusTypeEnum.getModbusTypeEnum(modbusType);

        String registerType = locator.getRegisterType();
        RegisterTypeEnum registerTypeEnum = RegisterTypeEnum.getRegisterTypeEnum(registerType);

        WriteValueVo writeValue = locator.getWriteValue();
        if (writeValue == null) {
            throw new TiotRuntimeException(ModbusErrorCodeEnum.WRITE_PARAM_EMPTY_ERROR);
        }
        String writeValueType = writeValue.getType();
        WriteValueTypeEnum writeValueTypeEnum = WriteValueTypeEnum.getWriteValueTypeEnum(writeValueType);
        Object value = writeValue.getValue();

        ModbusParamsUtil.checkRegisterTypeMatchValueType(registerTypeEnum, writeValueTypeEnum, true);
        ModbusParamsUtil.checkValueTypeMatchValue(writeValueTypeEnum, value);

        ModbusSlaveSet modbusSlave = modbusSlaveHolder.getModbusSlave(locator.getName(), modbusTypeEnum);
        if (modbusSlave == null) {
            throw new TiotRuntimeException(ModbusErrorCodeEnum.MODBUS_SLAVE_NOT_FOUND);
        }
        BasicProcessImage processImage = null;
        processImage = (BasicProcessImage) modbusSlave.getProcessImage(locator.getSlaveId());
        if (processImage == null) {
            processImage = new BasicProcessImage(locator.getSlaveId());
        }

        if (WriteValueTypeEnum.BOOLEAN.equals(writeValueTypeEnum)) {
            if (RegisterTypeEnum.COIL_INPUT.equals(registerTypeEnum)) {
                processImage.setCoil(locator.getOffset(), (boolean) value);
            } else if (RegisterTypeEnum.DISCRETE_INPUT.equals(registerTypeEnum)) {
                processImage.setInput(locator.getOffset(), (boolean) value);
            }
        } else if (WriteValueTypeEnum.SHORT.equals(writeValueTypeEnum)) {
            if (RegisterTypeEnum.HOLDING_REGISTER.equals(registerTypeEnum)) {
                processImage.setHoldingRegister(locator.getOffset(), ((Integer) value).shortValue());
            } else if (RegisterTypeEnum.INPUT_REGISTER.equals(registerTypeEnum)) {
                processImage.setInputRegister(locator.getOffset(), ((Integer) value).shortValue());
            }
        } else if (WriteValueTypeEnum.NUMBER.equals(writeValueTypeEnum)) {
            if (RegisterTypeEnum.HOLDING_REGISTER.equals(registerTypeEnum)) {
                processImage.setNumeric(RegisterTypeEnum.HOLDING_REGISTER.getRange(), locator.getOffset(), writeValue.getDataType(), (Number) value);
            } else if (RegisterTypeEnum.INPUT_REGISTER.equals(registerTypeEnum)) {
                processImage.setNumeric(RegisterTypeEnum.INPUT_REGISTER.getRange(), locator.getOffset(), writeValue.getDataType(), (Number) value);
            }
        } else if (WriteValueTypeEnum.BOOLEANS.equals(writeValueTypeEnum)) {
            List<Boolean> list = (List<Boolean>) value;
            boolean[] booleans = ModbusConvertUtil.convertToBooleans(list);
            if (RegisterTypeEnum.COIL_INPUT.equals(registerTypeEnum)) {
                for (int i = 0; i < booleans.length; i++) {
                    processImage.setCoil(locator.getOffset() + i, booleans[i]);
                }
            } else if (RegisterTypeEnum.DISCRETE_INPUT.equals(registerTypeEnum)) {
                for (int i = 0; i < booleans.length; i++) {
                    processImage.setInput(locator.getOffset() + i, booleans[i]);
                }
            }
        } else if (WriteValueTypeEnum.SHORTS.equals(writeValueTypeEnum)) {
            List<Integer> list = (List<Integer>) value;
            short[] shorts = ModbusConvertUtil.convertToShorts(list);
            if (RegisterTypeEnum.HOLDING_REGISTER.equals(registerTypeEnum)) {
                processImage.setHoldingRegister(locator.getOffset(), shorts);
            } else if (RegisterTypeEnum.INPUT_REGISTER.equals(registerTypeEnum)) {
                processImage.setInputRegister(locator.getOffset(), shorts);
            }
        } else if (WriteValueTypeEnum.NUMBERS.equals(writeValueTypeEnum)) {
            List<Number> list = (List<Number>) value;
            int registerCount = ModbusConvertUtil.getRegisterCount(writeValue.getDataType());
            if (RegisterTypeEnum.HOLDING_REGISTER.equals(registerTypeEnum)) {
                for (int i = 0; i < list.size(); i++) {
                    processImage.setNumeric(RegisterTypeEnum.HOLDING_REGISTER.getRange(), locator.getOffset() + i * registerCount, writeValue.getDataType(), list.get(i));
                }
            } else if (RegisterTypeEnum.INPUT_REGISTER.equals(registerTypeEnum)) {
                for (int i = 0; i < list.size(); i++) {
                    processImage.setNumeric(RegisterTypeEnum.INPUT_REGISTER.getRange(), locator.getOffset() + i * registerCount, writeValue.getDataType(), list.get(i));
                }
            }
        }

        ModbusSlaveUtil.setModbusSlave(modbusSlave, processImage);
    }

    @Override
    public SlaveImageVo readSlaveStatus(ModbusLocatorVo locator) {
        if (locator == null) {
            throw new TiotRuntimeException(ModbusErrorCodeEnum.PARAM_EMPTY_ERROR);
        }
        if (StringUtils.isBlank(locator.getName())) {
            throw new TiotRuntimeException(ModbusErrorCodeEnum.SLAVE_NAME_EMPTY_ERROR);
        }
        if (locator.getSlaveId() == null) {
            throw new TiotRuntimeException(ModbusErrorCodeEnum.SLAVE_ID_EMPTY_ERROR);
        }

        String modbusType = locator.getModbusType();
        ModbusTypeEnum modbusTypeEnum = ModbusTypeEnum.getModbusTypeEnum(modbusType);
        ModbusSlaveSet modbusSlave = modbusSlaveHolder.getModbusSlave(locator.getName(), modbusTypeEnum);
        if (modbusSlave == null) {
            throw new TiotRuntimeException(ModbusErrorCodeEnum.MODBUS_SLAVE_NOT_FOUND);
        }
        BasicProcessImage processImage = (BasicProcessImage) modbusSlave.getProcessImage(locator.getSlaveId());

        SlaveImageVo slaveImageVo = new SlaveImageVo();
        if (processImage == null) {
            return slaveImageVo;
        }
        // 利用反射获取属性值
        Field[] fields = processImage.getClass().getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            try {
                field.setAccessible(true);  // 设置这些属性是可以访问的
                Object o = field.get(processImage);
                if (field.getName().equals("coils")) {
                    slaveImageVo.setCoils((Map<Integer, Boolean>) o);
                } else if (field.getName().equals("inputs")) {
                    slaveImageVo.setInputs((Map<Integer, Boolean>) o);
                } else if (field.getName().equals("holdingRegisters")) {
                    slaveImageVo.setHoldingRegisters((Map<Integer, Short>) o);
                } else if (field.getName().equals("inputRegisters")) {
                    slaveImageVo.setInputRegisters((Map<Integer, Short>) o);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        return slaveImageVo;
    }
}
